package birdsVSZombiesServer;

import java.util.ArrayList;
import java.util.LinkedList;

import birdsVSZombiesClient.GUI.BVZButton;
import birdsVSZombiesClient.GUI.BVZRectButton;

import com.google.gson.Gson;

import pbox2d.PBox2D;
import jbox2dprocessing.compontent.*;

import processing.core.PApplet;
import processing.core.PFont;
import processing.net.Client;
import processing.net.Server;
import shared.BVZClientToServerMessages;
import shared.BVZConstants;
import shared.BVZServerToClientsMessages;

/**
 * The server class which runs the server and physical simulation.
 * @author Yang Liu
 * @version Version 0.0.4, 11/22/2010
 */
public class BirdsVSZombiesServerThread extends PApplet{
	
	public Server bvzServer;
	
	public int testTimer = 0;
	
	/*
	 * Variables for Server Status
	 */
	public int serverStat = INITCONNECTED;
	public static final int INITCONNECTED = 0;
	public static final int INITPHYS = 1;
	public static final int RUNPHYS = 2;
	
	/*
	 * Variables for Player Information
	 */
//	public LinkedList<BVZPlayer> bvzPlayerList = new LinkedList<BVZPlayer>();
	public BVZPlayer[] bvzPlayerList = new BVZPlayer[2];
	public int bvzPlayerNum = 0;
	
	/*
	 * Variables for physical stuff
	 */
	public PBox2D box2d;
	public LinkedList<BaseBox> boxes;
	public LinkedList<BoundaryBox> boundaries;
	public LinkedList<BaseCircle> circles;
	
	public MouseInteraction[] mouseJoints = new MouseInteraction[2];
	
	/*
	 * Variables for mouse listener thread
	 */
	public BVZMouseListener shooterListener;
	public BVZMouseListener defenderListener;
	public BVZMouseListener[] listeners = new BVZMouseListener[2];
	
	/*
	 * Variables for physical thread
	 */
	public PhysicalSimulation physicalSim;
	
	/*
	 * Variables for Json Messages
	 */
	public Gson gson;
	public BVZClientToServerMessages incomingMessage;
	public BVZServerToClientsMessages outcomingMessage;
	
	/*
	 * Server GUI
	 */
	public BVZRectButton startButton;
	public PFont f;
	
	
	public void setup(){
		size(BVZConstants.GAMEWINDOWWIDTH, BVZConstants.GAMEWINDOWHEIGHT);
		frameRate(BVZConstants.GAMEFRAMERATE);
		bvzServer = new Server(this, BVZConstants.GAMESERVERPORT);
		f = createFont("Arial", 20, true);
		
		AbstractBox2DShape.parent = this;
		BVZButton.parent = this;
		BVZMouseListener.parent = this;
		BVZServerToClientsMessages.parent = this;
		PhysicalSimulation.parent = this;
		
		gson = new Gson();
		
//		startButton = new BVZRectButton("Click to start!");
		
		listeners[0] = new BVZMouseListener();
		listeners[0].start();
		
		physicalSim = new PhysicalSimulation();
		
	}
	
	public void draw(){
		background(255);
		smooth();
		
		serverSelfUpdate();
		
//		startButton.display();
//		startButton.update();
	}
	
	
	/**
	 * Update server according to different server status.
	 */
	public void serverSelfUpdate(){
		switch (serverStat) {
			case INITCONNECTED:
				break;
			case INITPHYS:
				initPhysWorld();
				break;
			case RUNPHYS:
				runASimplePhys();
				break;
			default:
				break;
		}
	}
	
	/**
	 * Listen to the messages from clients.
	 * Replaced by the listener thread.
	 */
	public void listenToClients(){
//		Client bvzClient = bvzServer.available();
//		if(bvzClient != null){
//			String dataIn = bvzClient.readStringUntil('*');
//			if(dataIn != null){
//				dataIn = dataIn.substring(0, dataIn.length() -1);
//				incomingMessage = gson.fromJson(dataIn, BVZClientToServerMessages.class);
//				
////				println("Client says: " + dataIn);
//				
//				switch (incomingMessage.clientStat) {
//					case BVZConstants.CLIENTSTART:
//						makeBVZPlayer(bvzClient, 0);
//						makeBVZPlayer(bvzClient, 1);
//						println("Clients: "+ bvzClient.ip() +" started!!!");
//						//	A test for server phys simulation
//						callRunASimplePhys();
//						
//						break;
//					case BVZConstants.CLIENTJOININ:
//						makeBVZPlayer(bvzClient, incomingMessage.playerRole);
//						if(this.bvzPlayerNum == 2)
//							callRunASimplePhys();
//						break;
//					case BVZConstants.CLIENTRUNNINGPHYS:
//						switch (incomingMessage.mouseStat) {
//							case BVZConstants.MOUSEPRESSED:
//								createMouseJoint();
//								break;
//							case BVZConstants.MOUSEDRAGGED:
//								updateMouseJoint();
//								break;
//							case BVZConstants.MOUSERELEASED:
//								destoryMouseJoint();
//								break;
//							default:
//								break;
//						}
//						
//						break;
//					default:
//						break;
//				}
//			}
//			else{
//				println("No data Recieved!!!");
//			}
//		}
	}
	
	/**
	 * Change the server status to start the physical simulation.
	 */
	public void callRunASimplePhys(){
		serverStat = INITPHYS;
		
	}
	
	/**
	 * Initialize the physical world.
	 */
	public void initPhysWorld(){
		box2d = new PBox2D(this);
		box2d.createWorld();
		box2d.setGravity(0, -50);
		
		boundaries = new LinkedList<BoundaryBox>();
		boxes = new LinkedList<BaseBox>();
		circles = new LinkedList<BaseCircle>();
		
		//	Initial the environment 
		BoundaryBox bottomBoundary = new BoundaryBox(600, 550, 1200, 100, box2d, BVZConstants.NOROLE);
		boundaries.add(bottomBoundary);
				
		BoundaryBox bottomBoundary3 = new BoundaryBox(0, 250, 50, 500, box2d, BVZConstants.NOROLE);
		boundaries.add(bottomBoundary3);
		
		BoundaryBox bottomBoundary4 = new BoundaryBox(1200, 250, 50, 500, box2d, BVZConstants.NOROLE);
		boundaries.add(bottomBoundary4);
		
//		PhysCircle p = new PhysCircle(100, height/2, 23, box2d, bvzPlayerList[0].playerRole, 1.0f, 0.3f, 0.3f);
//		circles.add(p);
//		
//		PhysCircle p2 = new PhysCircle(200, height/2, 23, box2d, bvzPlayerList[0].playerRole, 1.0f, 0.3f, 0.3f);
//		circles.add(p2);
		
		//	Initial the birds shooter
		NormalBird nb = new NormalBird(box2d, 100, 300);
//		BlackBird bb = new BlackBird(box2d, 200, 200);
//		WhiteBird wb = new WhiteBird(box2d, 150, 400);
//		ToughBird tb = new ToughBird(box2d, 130, 100);
		circles.add(nb);
//		circles.add(bb);
//		circles.add(wb);
//		circles.add(tb);
		
		//	Initial the zombies defender
		NormalZombie nz1 = new NormalZombie(box2d, 900.0f, 300.0f);
		circles.add(nz1);
//		PunkZombie nz2 = new PunkZombie(box2d, 950.0f, 300.0f);
//		circles.add(nz2);
//		JacksonZombie nz3 = new JacksonZombie(box2d, 850.0f, 300.0f);
//		circles.add(nz3);
//		
//		WoodBlockSmall wbs1 = new WoodBlockSmall(box2d, 1000.0f, 300.0f);
//		boxes.add(wbs1);
//		WoodBlockLarge wbs2 = new WoodBlockLarge(box2d, 900.0f, 360.0f);
//		boxes.add(wbs2);
//		StoneBlockFrame wbs5 = new StoneBlockFrame(box2d, 850.0f, 360.0f);
//		boxes.add(wbs5);
//		IceBlockMedium wbs6 = new IceBlockMedium(box2d, 900.0f, 400.0f);
//		boxes.add(wbs6);
//		IceBlockFrame wbs7 = new IceBlockFrame(box2d, 900.0f, 120.0f);
//		boxes.add(wbs7);

		
		BVZServerToClientsMessages msg = new BVZServerToClientsMessages(BVZConstants.SERVERPHYSINITCOMPLETED);
		String msgJson = gson.toJson(msg) + "*";
		bvzServer.write(msgJson);
		
//		physicalSim.start();
		
		serverStat = RUNPHYS;
	}
	
	/**
	 * Run a simple physical simulation and send data to Clients.
	 */
	public void runASimplePhys(){
		box2d.step();
		
		for (int i = 0; i < boundaries.size(); i++) {
			BoundaryBox bb = boundaries.get(i);
			bb.display();
		}
		
		//		Display the PhysCircles
		for (int i = 0; i < circles.size(); i++) {
			BaseCircle c = (BaseCircle)circles.get(i);
			c.display();
		}
		
		for (int i = 0; i < boxes.size(); i++) {
			BaseBox b = (BaseBox)boxes.get(i);
			b.display();
		}
		
		//		Make a snap shot of the scene
		this.testTimer++;
		BVZServerToClientsMessages msg = new BVZServerToClientsMessages(this.testTimer, BVZConstants.SERVERRUNPHYS, boundaries, circles, boxes);
		String msgJson = gson.toJson(msg) + "*";
//		println("send!!!!!");
				
		bvzServer.write(msgJson);
//		println(msgJson);
	}
	
	public void serverEvent(Server someServer, Client someClient){
		//		Make a welcome json message for new connecter
		BVZServerToClientsMessages msg = new BVZServerToClientsMessages(BVZConstants.SERVERWELCOMEMESSAGE);
		String msgJson = gson.toJson(msg) + "*";
		
		println(msgJson);
		
		someClient.write(msgJson);
	}
	
	/**
	 * Add players when a new client connected.
	 * @param bvzClient The clients which the current player is in.
	 */
	public void makeBVZPlayer(Client bvzClient, int playerRole){
		BVZPlayer bvzPlayer = new BVZPlayer(playerRole, bvzClient);
		bvzPlayerList[playerRole] = bvzPlayer;
				
		println("we have a new " + bvzPlayer.playerTitle + "!" + bvzPlayerList[playerRole].playerClient.ip());
		
		bvzPlayerNum++;
	}
	
	/**
	 * Construct the message the server will send back to clients
	 */
	public void makeMessages(){
		// TODO make this function
	}
	
	/**
	 * Create a mouse joint only when the shape is belong to the player.
	 */
	public void createMouseJoint(){
		mouseJoints[incomingMessage.playerRole] = new MouseInteraction(box2d);
		if(mouseJoints[incomingMessage.playerRole].getBodyAtClientMouse(incomingMessage) != null && mouseJoints[incomingMessage.playerRole].getBodyAtClientMouse(incomingMessage).bodyMaster == incomingMessage.playerRole){
			mouseJoints[incomingMessage.playerRole].bind(incomingMessage.mousePosX, incomingMessage.mousePosY, mouseJoints[incomingMessage.playerRole].getBodyAtClientMouse(incomingMessage));
			mouseJoints[incomingMessage.playerRole].update(incomingMessage.mousePosX, incomingMessage.mousePosY);
			println("Mouse Joint Created!!!!");
		}
	}
	
	/**
	 * Update a mouse joint
	 */
	public void updateMouseJoint(){
		mouseJoints[incomingMessage.playerRole].update(incomingMessage.mousePosX, incomingMessage.mousePosY);
	}
	
	/**
	 * Destory a mouse joint
	 */
	public void destoryMouseJoint(){
		mouseJoints[incomingMessage.playerRole].destory();
		println("Mouse Joint Destoried!!!!");
	}
	
	public static void main(String _args[]) {
		PApplet.main(new String[] { birdsVSZombiesServer.BirdsVSZombiesServerThread.class.getName() });
	}
}
